// examples目录是官方的一些例子
// 本次使用的是webSocket_server\webSocket_server.cpp
// 该原程序只支持一对一发送后回复
// 改造后可以通知所有连接上来的客户端。
// 编译 g++ main.cpp -o main -lboost_system -lboost_chrono

#include <functional>
#include <iostream>
#include <list>
#include <mutex> // 添加互斥锁头文件
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <string>

// json解析
#include <nlohmann/json.hpp>

#include "signal_type.h"
using json = nlohmann::json;

typedef websocketpp::server<websocketpp::config::asio> server;

using websocketpp::lib::bind;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;

// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;

const int LISTEN_PORT = 9002;
std::list<websocketpp::connection_hdl> vgdl;
std::mutex vgdl_mutex; // 添加互斥锁保护连接列表

struct Client
{
    std::string uid;
    int roomId;
    websocketpp::connection_hdl hdl; // 连接句柄
};

std::map<int, std::vector<Client>> room_map; // roomId - client

// Define a callback to handle incoming messages

// Create a server endpoint
server webSocket_server;

int totalUser = 0;
void send_msg(server *s, message_ptr msg)
{
    for (auto it = vgdl.begin(); it != vgdl.end();)
    {
        if (!it->expired())
        {
            try
            {
                s->send(*it, msg->get_payload(), msg->get_opcode());
            }
            catch (websocketpp::exception const &e)
            {
                std::cout << "Broadcast failed because: " << e.what()
                          << std::endl;
            }
            ++it; // 只有在未删除元素时才递增迭代器
        }
    }
}

void send_msg(server *s, std::string msg)
{
    for (auto it = vgdl.begin(); it != vgdl.end();)
    {
        if (!it->expired())
        {
            try
            {
                s->send(*it, msg, websocketpp::frame::opcode::text);
            }
            catch (websocketpp::exception const &e)
            {
                std::cout << "Broadcast failed because: " << e.what()
                          << std::endl;
            }
            ++it; // 只有在未删除元素时才递增迭代器
        }
    }
}

// 处理加入房间
void handleJoin(server *s, websocketpp::connection_hdl hdl, json JMsg)
{
    // 解析JSON
    std::string uid = JMsg["uid"];
    std::string roomStr = JMsg["roomId"];
    int roomId = stoi(roomStr);

    std::cout << "uid = " << uid << " try to join roomId = " << roomId << std::endl;

    // 获取房间信息

    // 房间不存在
    if (!room_map.count(roomId))
    {
        Client client = {uid, roomId, hdl};
        room_map[roomId].push_back(client);
    }
    else
    {
        // 房间人数>=2，不允许加入
        if (room_map[roomId].size() >= 2)
        {
            std::cout << "roomId = " << roomId << "is full" << std::endl;
            return;
        }
        // 房间人数==1，加入房间，通知对端
        else if (room_map[roomId].size() == 1)
        {
            Client client = {uid, roomId, hdl};
            room_map[roomId].push_back(client);

            // 处理信令
            Client remoteClient = room_map[roomId][0];

            // 告知加入者对端的信息
            json sendMsg;
            sendMsg["cmd"] = SIGNAL_TYPE_RESP_JOIN;
            sendMsg["remoteUid"] = remoteClient.uid;
            std::string sendMsgStr = sendMsg.dump();
            s->send(client.hdl, sendMsgStr, websocketpp::frame::opcode::text);
            std::cout << "resp_join uid = " << remoteClient.uid << std::endl;
            std::cout << "sendMsgStr = " << sendMsgStr << std::endl;

            // 告知对端加入者的身份
            json sendMsg2;
            sendMsg2["cmd"] = SIGNAL_TYPE_NEW_PEER;
            sendMsg2["remoteUid"] = uid;
            std::string sendMsgStr2 = sendMsg2.dump();
            s->send(remoteClient.hdl, sendMsgStr2, websocketpp::frame::opcode::text);
            std::cout << "new_peer uid = " << uid << std::endl;
            std::cout << "sendMsgStr = " << sendMsgStr2 << std::endl;
        }
    }
}

// 处理离开房间
void handleLeave(server *s, websocketpp::connection_hdl hdl, json JMsg)
{
    std::string roomStr = JMsg["roomId"];
    int roomId = stoi(roomStr);
    std::string uid = JMsg["uid"];

    std::cout << "uid = " << uid << " try to leave roomId = " << roomId << std::endl;

    // 找不到房间
    if (!room_map.count(roomId))
    {
        std::cout << "房间不存在 !" << std::endl;
        return;
    }
    else
    {
        // 房间内只有一个人,删除房间
        if (room_map[roomId].size() == 1)
        {
            room_map.erase(roomId);
            std::cout << "erase roomId = " << roomId << "success" << std::endl;
        }
        // 房间有两个人,通知对端离开
        else if (room_map[roomId].size() == 2)
        {
            // 删除用户信息
            auto iter = std::find_if(room_map[roomId].begin(), room_map[roomId].end(), [&uid](const Client &client)
                                     { return client.uid == uid; });

            if (iter != room_map[roomId].end())
            {
                room_map[roomId].erase(iter);
                std::cout << "erase uid = " << uid << "success" << std::endl;
            }

            // 发送JSON消息
            json sendMsg;
            sendMsg["cmd"] = SIGNAL_TYPE_PEER_LEAVE;
            sendMsg["remoteUid"] = uid;

            std::string sendMsgStr = sendMsg.dump();
            // 只有一个人了，使用room_map[roomId][0]
            s->send(room_map[roomId][0].hdl, sendMsgStr, websocketpp::frame::opcode::text);
            std::cout << "resp_leave uid = " << uid << std::endl;
            std::cout << "sendMsgStr = " << sendMsgStr << std::endl;
        }
    }
}

// offer
void handleOffer(server *s, websocketpp::connection_hdl hdl, json JMsg)
{
    std::string roomStr = JMsg["roomId"];
    int roomId = stoi(roomStr);
    std::string uid = JMsg["uid"];
    std::string remoteUid = JMsg["remoteUid"];

    std::cout << "uid = " << uid << " try to send offer to remoteUid = " << remoteUid << std::endl;

    // 房间号不存在
    if (!room_map.count(roomId))
    {
        std::cout << "roomId = " << roomId << "not exist" << std::endl;
        return;
    }
    // 房间没人
    else if (room_map[roomId].size() == 0)
    {
        std::cout << "roomId = " << roomId << "is empty" << std::endl;
        return;
    }
    else
    {
        // 转发offer到对端
        auto remoteClientIter = std::find_if(room_map[roomId].begin(), room_map[roomId].end(), [&remoteUid](const Client &client)
                                             { return client.uid == remoteUid; });

        if (remoteClientIter != room_map[roomId].end())
        {
            std::cout << "send offer from " << uid << " to " << remoteUid << std::endl;
            s->send(remoteClientIter->hdl, JMsg.dump(), websocketpp::frame::opcode::text);
        }
        else
        {
            std::cout << "remoteUid = " << remoteUid << "not exist" << std::endl;
        }
    }
}

// answer
void handleAnswer(server *s, websocketpp::connection_hdl hdl, json JMsg)
{
    std::string roomStr = JMsg["roomId"];
    int roomId = stoi(roomStr);
    std::string uid = JMsg["uid"];
    std::string remoteUid = JMsg["remoteUid"];

    std::cout << "uid = " << uid << " try to send answer to remoteUid = " << remoteUid << std::endl;

    // 房间号不存在
    if (!room_map.count(roomId))
    {
        std::cout << "roomId = " << roomId << "not exist" << std::endl;
        return;
    }
    // 房间没人
    else if (room_map[roomId].size() == 0)
    {
        std::cout << "roomId = " << roomId << "is empty" << std::endl;
        return;
    }
    else
    {
        // 转发answer到对端
        auto remoteClientIter = std::find_if(room_map[roomId].begin(), room_map[roomId].end(), [&remoteUid](const Client &client)
                                             { return client.uid == remoteUid; });

        if (remoteClientIter != room_map[roomId].end())
        {
            std::cout << "send answer from " << uid << " to " << remoteUid << std::endl;
            s->send(remoteClientIter->hdl, JMsg.dump(), websocketpp::frame::opcode::text);
        }
        else
        {
            std::cout << "remoteUid = " << remoteUid << "not exist" << std::endl;
        }
    }
}

// candidate
void handleCandidate(server *s, websocketpp::connection_hdl hdl, json JMsg)
{
    std::string roomStr = JMsg["roomId"];
    int roomId = stoi(roomStr);
    std::string uid = JMsg["uid"];
    std::string remoteUid = JMsg["remoteUid"];

    std::cout << "uid = " << uid << " try to send candidate to remoteUid = " << remoteUid << std::endl;

    // 房间号不存在
    if (!room_map.count(roomId))
    {
        std::cout << "roomId = " << roomId << "not exist" << std::endl;
        return;
    }
    // 房间没人
    else if (room_map[roomId].size() == 0)
    {
        std::cout << "roomId = " << roomId << "is empty" << std::endl;
        return;
    }
    else
    {
        // 转发candidate到对端
        auto remoteClientIter = std::find_if(room_map[roomId].begin(), room_map[roomId].end(), [&remoteUid](const Client &client)
                                             { return client.uid == remoteUid; });

        if (remoteClientIter != room_map[roomId].end())
        {
            std::cout << " send candidate from " << uid << " to " << remoteUid << std::endl;
            s->send(remoteClientIter->hdl, JMsg.dump(), websocketpp::frame::opcode::text);
        }
        else
        {
            std::cout << "remoteUid = " << remoteUid << "not exist" << std::endl;
        }
    }
}

// WebSocket服务器收到消息回调
void on_message(server *s, websocketpp::connection_hdl hdl, message_ptr msg)
{
    // 解析客户端的json消息
    json JMsg;
    try
    {
        JMsg = json::parse(msg->get_payload());
        std::cout << "on_message called with hdl: " << hdl.lock().get()
                  << " and message: " << JMsg.dump() << std::endl;

        std::string cmd = JMsg["cmd"];
        if (cmd == SIGNAL_TYPE_JOIN)
        {
            handleJoin(s, hdl, JMsg); // 加入
        }
        else if (cmd == SIGNAL_TYPE_LEAVE)
        {
            handleLeave(s, hdl, JMsg); // 离开
        }
        else if (cmd == SIGNAL_TYPE_OFFER)
        {
            handleOffer(s, hdl, JMsg); // ice候选
        }
        else if (cmd == SIGNAL_TYPE_ANSWER)
        {
            handleAnswer(s, hdl, JMsg);
        }
        else if (cmd == SIGNAL_TYPE_CANDIDATE)
        {
            handleCandidate(s, hdl, JMsg);
        }
    }
    catch (const std::exception &e)
    {
        std::cout << "JSON解析失败: " << e.what() << std::endl;
        return;
    }
}

// 当有客户端连接时触发的回调
void on_open(websocketpp::connection_hdl hdl)
{
    vgdl.push_back(hdl);

    std::cout << "on_open called with hdl: " << hdl.lock().get() << std::endl;
}

// 用户断开连接回调函数
void on_close(websocketpp::connection_hdl hdl)
{
    std::string msg = "close OK";
    printf("%s\n", msg.c_str());

    std::cout << "vgdl size = " << vgdl.size() << std::endl;
    // 清理连接列表
    for (auto it = vgdl.begin(); it != vgdl.end();)
    {
        std::cout << "it = " << it->lock() << std::endl;
        if (it->expired() || it->lock() == hdl.lock()) //断开自己
        {
            it = vgdl.erase(it);
            std::cout << "vgdl erase" << std::endl;
        }
        else
        {
            ++it;
        }
    }

    // 遍历 room_map，删除对应客户端信息
    for (auto roomIt = room_map.begin(); roomIt != room_map.end();)
    {
        auto &clients = roomIt->second;
        bool isErase = false;
        for (auto clientIt = clients.begin(); clientIt != clients.end();)
        {
            if (clientIt->hdl.expired() || clientIt->hdl.lock() == hdl.lock())
            { // 连接过期
                std::cout << "client uid = " << clientIt->uid << " has been removed from roomid = "
                          << clientIt->roomId << std::endl;

                clientIt = clients.erase(clientIt);
                isErase = true;
            }
            else
            {
                ++clientIt;
            }
        }

        if(!isErase){
            continue;
        }

        // 如果房间为空，删除房间
        if (clients.empty())
        {
            std::cout << "roomId = " << (roomIt->first) << " has been removed" << std::endl;
            roomIt = room_map.erase(roomIt);
        }
        else
        {
            //向对端发送离开消息
            json sendMsg;
            sendMsg["cmd"] = SIGNAL_TYPE_PEER_LEAVE;
            sendMsg["remoteUid"] = roomIt->second[0].uid;
            send_msg(&webSocket_server, sendMsg.dump());

            ++roomIt;
        }
    }
}

int main()
{
    try
    {
        // Set logging settings 设置log
        webSocket_server.set_access_channels(websocketpp::log::alevel::all);
        webSocket_server.clear_access_channels(
            websocketpp::log::alevel::frame_payload);

        // Initialize Asio 初始化asio
        webSocket_server.init_asio();

        // Register our message handler
        // 绑定收到消息后的回调
        webSocket_server.set_message_handler(
            bind(&on_message, &webSocket_server, ::_1, ::_2));
        // 当有客户端连接时触发的回调
        std::function<void(websocketpp::connection_hdl)> f_open;
        f_open = on_open;
        webSocket_server.set_open_handler(websocketpp::open_handler(f_open));
        // 关闭是触发
        std::function<void(websocketpp::connection_hdl)> f_close(on_close);
        webSocket_server.set_close_handler(f_close);
        // Listen on port 9002
        webSocket_server.listen(LISTEN_PORT); // 监听端口

        // Start the server accept loop
        webSocket_server.start_accept();

        // Start the ASIO io_service run loop
        std::cout << "Server is running on port " << LISTEN_PORT << std::endl;
        webSocket_server.run();
    }
    catch (websocketpp::exception const &e)
    {
        std::cout << e.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "other exception" << std::endl;
    }
}